#include "xicmp.h"

void xicmp_init(void){
    // 无操作

};

void xicmp_in(xipaddr_t *src_ip, xnet_packet_t *packet)
{
    if (ICMP_GDB_INFO)
    {
        char ip_str[32];
        ip_to_str(src_ip, ip_str, XNET_IPV4_ADDR_SIZE);
        print_info("get a icmp packet from %s", ip_str);
    }
    xicmp_hdr_t *icmp_hdr = (xicmp_hdr_t *)packet->data;
    if ((packet->size >= sizeof(xicmp_hdr_t)) && (icmp_hdr->type == XICMP_CODE_ECHO_REQUEST))
    {
        // 8位的数就不做转换
        reply_icmp_request(icmp_hdr, src_ip, packet);
    }
};
xnet_err_t reply_icmp_request(xicmp_hdr_t *icmp_hdr, xipaddr_t *src_ip, xnet_packet_t *origin_packet)
{
    xnet_packet_t *packet = xnet_alloc_for_send(origin_packet->size);
    xicmp_hdr_t *reply_hdr = (xicmp_hdr_t *)packet->data;

    // 除了type其他照抄，要重新计算校验和
    reply_hdr->code = icmp_hdr->code;
    reply_hdr->id = icmp_hdr->id;
    reply_hdr->seq = icmp_hdr->seq;
    reply_hdr->type = XICMP_CODE_ECHO_REPLY;
    reply_hdr->checksum = 0; // 一定要置0，不然下一次的不对
    // 数据照抄
    memcpy((uint8_t *)packet->data + sizeof(xicmp_hdr_t), (uint8_t *)origin_packet->data + sizeof(xicmp_hdr_t),
           packet->size - sizeof(xicmp_hdr_t));

    reply_hdr->checksum = checksum16((uint16_t *)reply_hdr, packet->size, 0, 1);
    if (ICMP_GDB_INFO)
    {
        char ip_str[32];
        ip_to_str(src_ip, ip_str, XNET_IPV4_ADDR_SIZE);
        print_info("try to send a icmp packet to %s", ip_str);
    }
    return xip_out(XNET_PROTOCOL_ICMP, src_ip, packet);
};

xnet_err_t xicmp_dest_unreach(xicmp_unreach_type code, xip_hdr_t *ip_hdr)
{
    // 协议不可达

    // 申请包，总长度减去ip头的长度
    uint16_t data_size = swap_order16(ip_hdr->total_len) - ip_hdr->total_len * 4;
    xnet_packet_t *packet = xnet_alloc_for_send(data_size);
    xicmp_hdr_t *reply_hdr = (xicmp_hdr_t *)packet->data;

    // 除了type其他照抄，要重新计算校验和
    reply_hdr->code = code;
    reply_hdr->id = 0;
    reply_hdr->seq = 0;
    reply_hdr->type = XICMP_TYPE_UNREACH;
    reply_hdr->checksum = 0; // 一定要置0，不然下一次的不对
    // 数据照抄
    memcpy((uint8_t *)packet->data + sizeof(xicmp_hdr_t), (uint8_t *)ip_hdr + ip_hdr->total_len * 4, data_size);

    reply_hdr->checksum = checksum16((uint16_t *)reply_hdr, packet->size, 0, 1);
    if (ICMP_GDB_INFO)
    {
        char ip_str[32];
        ip_to_str(ip_hdr->src_ip, ip_str, XNET_IPV4_ADDR_SIZE);
        print_info("try to send a unreach icmp packet to %s", ip_str);
    }
    return xip_out(XNET_PROTOCOL_ICMP, ip_hdr->src_ip, packet);
};
